﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using blackhouse.BlackApart.Business.ApartmentServices;
using blackhouse.BlackApart.Business.Opinions;
using blackhouse.BlackApart.Business.Payments;
using blackhouse.BlackApart.Business.Prices;
using blackhouse.BlackApart.Data;
using System.Web;
using System.Web.SessionState;

namespace blackhouse.BlackApart.Business.Reservations {
    public class DaWebReservation : IWebReservation, InterviewerReservations {

        #region Fields

        private ImpWebReservation instance;
        private IReservationDataAccess dataAccess;

        #endregion

        #region Properties

        public int ReservationId {
            get { return this.instance.ReservationId; }
            private set { this.instance.ReservationId = value; }
        }

        public string ReservationNumber {
            get { return this.instance.ReservationNumber; }
            private set { this.instance.ReservationNumber = value; }
        }

        public DateTime ArrivalDate {
            get { return this.instance.ArrivalDate; }
        }

        public DateTime DepartureDate {
            get { return this.instance.DepartureDate; }
        }

        public double Price {
            get { return this.instance.Price; }
            private set { this.instance.Price = value; }
        }

        public double FinalPrice {
            get { return this.instance.FinalPrice; }
        }

        public float Rabat {
            get { return this.instance.Rabat; }
        }

        public bool VatInvoice {
            get { return this.instance.VatInvoice; }
            set { this.instance.VatInvoice = value; }
        }

        public ReservationStatus Status {
            get { return this.instance.Status; }
            private set { this.instance.Status = value; }
        }

        public Guid Guid {
            get { return this.instance.Guid; }
            private set { this.instance.Guid = value; }
        }

        public ApartmentWebReservation[] ApartmentReservation {
            get { return this.instance.ApartmentReservation; }
        }

        public ReservationPriceRabat[] ReservationRabats {
            get { return this.instance.ReservationRabats; }
        }

        public string Comments {
            get { return this.instance.Comments; }
            set { this.instance.Comments = value; }
        }

        public string OfficeComments { get { return this.instance.OfficeComments; } }

        public int PersonCount {
            get {
                return this.instance.PersonCount;
            }
        }

        public IClient Client {
            get {
                if (this.instance.Client == null && this.instance.ClientId > 0)
                    this.instance.Client = Factory.Instance().GetClient(this.instance.ClientId);
                return this.instance.Client;
            }
        }

        public IPaymentType PaymentType {
            get {
                if (this.instance.PaymentType == null && this.instance.PaymentTypeId > 0)
                    this.instance.PaymentType = Factory.Instance().GetPaymentType(this.instance.PaymentTypeId);
                return this.instance.PaymentType;
            }
        }

        #endregion

        #region Constructors

        public DaWebReservation(IReservationDataAccess reservationDataAccess) {
            this.dataAccess = reservationDataAccess;
            this.instance = new ImpWebReservation();
        }

        public DaWebReservation(IReservationDataAccess reservationDataAccess, WebReservationData data) {
            this.dataAccess = reservationDataAccess;
            this.instance = this.ReadInstance(data);
            if (this.instance == null)
                this.instance = new ImpWebReservation();
        }

        public DaWebReservation(IReservationDataAccess reservationDataAccess, int reservationId) {
            this.dataAccess = reservationDataAccess;
            if (reservationId > 0)
                this.instance = this.GetInstance(reservationId);
            if (this.instance == null)
                this.instance = new ImpWebReservation();
        }

        public DaWebReservation(IReservationDataAccess reservationDataAccess, string reservationNumber) {
            this.dataAccess = reservationDataAccess;
            this.instance = this.GetInstance(reservationNumber);
            if (this.instance == null)
                this.instance = new ImpWebReservation();
        }

        public DaWebReservation(IReservationDataAccess reservationDataAccess, Guid reservationGuid) {
            this.dataAccess = reservationDataAccess;
            this.instance = this.GetInstance(reservationGuid);
            if (this.instance == null)
                this.instance = new ImpWebReservation();
        }

        #endregion

        #region Methods

        public bool AddApartment(int apartmentId, int forPeoples) {
            return this.instance.AddApartment(apartmentId, forPeoples);
        }

        public void AddPriceRabat(IPriceRabat rabat) {
            this.instance.AddPriceRabat(rabat);
        }

        public void AddPriceRabat(IPriceRabat rabat, float overrideRabat) {
            this.instance.AddPriceRabat(rabat, overrideRabat);
        }

        public void SetDates(DateTime arrivalDate, DateTime departureDate) {
            this.instance.SetDates(arrivalDate, departureDate);
        }

        public void SetArrivalTime(TimeSpan arrivalTime) {
            this.instance.SetArrivalTime(arrivalTime);
        }

        public void SetClient(IClient client) {
            this.instance.SetClient(client);
            this.Save();
        }

        public void SetPaymentType(IPaymentType paymentType) {
            this.instance.SetPaymentType(paymentType);
            this.Save();
        }

        public bool Save() {
            if (this.ReservationId == 0)
                return this.SaveNew();
            else
                return this.Update();
        }

        private bool Update() {
            if (!this.instance.Save())
                return false;
            bool state = this.dataAccess.UpdateWebReservation(this.ReservationId, this.ArrivalDate, this.DepartureDate, this.FinalPrice, this.Price, this.Rabat, this.VatInvoice, this.Comments, this.Client != null ? this.Client.Id : 0, this.PaymentType != null ? this.PaymentType.Id : 0);
            if (state) this.SaveRabats();
            return state;

        }

        private bool SaveNew() {
            if (!this.instance.Save()) return false;

            // zapisz rezerwację
            WebReservationSaveData data = this.dataAccess.AddWebReservation(this.ArrivalDate, this.DepartureDate, this.FinalPrice, this.Price, this.Rabat);
            if (!data.Added) return false;

            this.ReservationId = data.ReservationId;
            this.ReservationNumber = data.ReservationNumber;
            this.Guid = data.Guid;

            try {
                foreach (ApartmentWebReservation apartmentReservation in this.ApartmentReservation) {
                    int recordId = this.dataAccess.AddApartmentToReservation(this.ReservationId, apartmentReservation.ApartmentId, apartmentReservation.PeopleCount);
                    if (recordId == 0)
                        throw new ExceptionAparts(5, null, new KeyValuePair<string, object>("apartmentTypeId", apartmentReservation.ApartmentId));
                    apartmentReservation.RecordId = recordId;
                }
            } catch (ExceptionData ed) {
                this.RollbackSaveNewReservation();
                if (ed.Code == 1)
                    throw new ExceptionNotFreeApartment(ed);
                else
                    throw;
            } catch (Exception e) {
                this.RollbackSaveNewReservation();
                throw new ExceptionAparts(5, e);
            }

            this.SaveRabats();

            // store to session
            HttpContext.Current.Session.Add("current_reservation", this.ReservationId);
            return true;
        }

        public void RemoveOpenReservation() {
            HttpSessionState sess = HttpContext.Current.Session;
            object cro = sess["current_reservation"];
            if (cro != null) {
                int reservationId = (int)cro;
                if (reservationId > 0) {
                    IWebReservation reservation = new DaWebReservation(this.dataAccess, reservationId);
                    if (reservation != null && reservation.Status.Key.Equals("unfinish", StringComparison.InvariantCultureIgnoreCase))
                        reservation.Remove();
                }
                HttpContext.Current.Session.Remove("current_reservation");
            }
        }

        private void SaveRabats() {
            foreach (ReservationPriceRabat rabat in this.ReservationRabats) {
                float rabatValue = rabat.Rabat.RabatValue;
                if (rabat.OverrideRabatValue != 0)
                    rabatValue = rabat.OverrideRabatValue;
                int recordId = this.dataAccess.AddRabatToReservation(this.ReservationId, rabat.Rabat.RabatKey, rabatValue);
                rabat.RecordId = recordId;
            }
        }

        private ImpWebReservation ReadInstance(WebReservationData data) {
            if (data.ReservationId == 0) return null;
            ImpWebReservation reservation = new ImpWebReservation();
            reservation.ArrivalDate = data.ArrivalDate;
            reservation.DepartureDate = data.DepartureDate;
            reservation.ReservationId = data.ReservationId;
            reservation.ReservationNumber = data.ReservationNumber;
            reservation.Price = data.Price;
            reservation.VatInvoice = data.VatInvoice;
            reservation.Status = this.ReadStatus(data);
            reservation.Guid = data.Guid;
            reservation.Comments = data.Comments;
            reservation.OfficeComments = data.OfficeComments;
            reservation.ClientId = data.ClientId;
            reservation.PaymentTypeId = data.PaymentTypeId;
            ApartmentWebReservationData[] apartments = this.dataAccess.GetApartmentsWebReservation(data.ReservationId);
            foreach (ApartmentWebReservationData apartmentData in apartments) {
                ApartmentWebReservation apartment = new ApartmentWebReservation(this, apartmentData);
                apartment.RecordId = apartmentData.RecordId;
                reservation.AddApartment(apartment);
            }
            Factory factory = Factory.Instance();
            ReservationPriceRabatData[] rabats = this.dataAccess.GetRabatsWebReservation(data.ReservationId, CultureInfo.CurrentCulture.Name);
            foreach (ReservationPriceRabatData rabat in rabats) {
                IPriceRabat r = factory.GetPriceRabat(rabat);
                ReservationPriceRabat rr = new ReservationPriceRabat(r);
                rr.RecordId = rabat.RecordId;
                rr.OverrideRabatValue = rabat.OverrideRabatValue;
                reservation.AddPriceRabat(rr);
            }
            return reservation;
        }

        private ImpWebReservation GetInstance(int reservationId) {
            WebReservationData data = this.dataAccess.GetWebReservation(reservationId, CultureInfo.CurrentCulture.Name);
            if (data.ReservationId == reservationId)
                return this.ReadInstance(data);
            else
                return null;
        }

        private ImpWebReservation GetInstance(Guid reservationGuid) {
            WebReservationData data = this.dataAccess.GetWebReservation(reservationGuid, CultureInfo.CurrentCulture.Name);
            if (data.Guid == reservationGuid)
                return this.ReadInstance(data);
            else
                return null;
        }

        private ImpWebReservation GetInstance(string reservationNumber) {
            WebReservationData data = this.dataAccess.GetWebReservation(reservationNumber, CultureInfo.CurrentCulture.Name);
            if (data.ReservationNumber == reservationNumber)
                return this.ReadInstance(data);
            else
                return null;
        }

        private void RollbackSaveNewReservation() {
            this.dataAccess.RollbackWebReservation(this.ReservationId);
            this.Status = default(ReservationStatus);
            this.ReservationId = 0;
            this.ReservationNumber = string.Empty;
            this.Guid = Guid.Empty;
        }

        public void Remove() {
            this.dataAccess.RollbackWebReservation(this.ReservationId);
            this.instance.Remove();
        }

        private ReservationStatus ReadStatus(WebReservationData data) {
            ReservationStatus status = new ReservationStatus();
            status.Id = data.StatusId;
            status.MainId = data.MainStatusId;
            status.Name = data.StatusName;
            status.Description = data.StatusDescription;
            status.Key = data.StatusKey;
            return status;
        }

        public void SetPrice(IApartmentPrice price) {
            this.instance.SetPrice(price);
        }

        public void ChangeStatus(string newStatusKey) {
            this.dataAccess.ChangeReservationStatus(this.ReservationId, newStatusKey);
        }

        public List<IWebReservation> FindReservationForInterviewer() {
            WebReservationData[] data = this.dataAccess.FindReservationForInterviewer();
            List<IWebReservation> reservations = new List<IWebReservation>(data.Length);
            for (int xd = 0; xd < data.Length; xd++) {
                reservations.Add(new DaWebReservation(this.dataAccess, data[xd]));
            }
            return reservations;
        }

        #endregion

    }
}
